// http://mongodb.github.io/node-mongodb-native/
// http://mongodb.github.io/node-mongodb-native/4.1/
const DB_URL = 'mongodb://localhost:27017';
const DB_NAME = 'test';

const { MongoClient, ObjectId } = require('mongodb');

class MongoSir {
  constructor() {}

  static getInstance() {
    if (!MongoSir.instance) {
      MongoSir.instance = new MongoSir();
    }
    return MongoSir.instance;
  }

  connect() {
    return new Promise((resolve, reject) => {
      MongoClient.connect(DB_URL, { useNewUrlParser: true }, (err, client) => {
        if (err) {
          reject(err);
        }
        const clientDB = client.db(DB_NAME);
        resolve(clientDB);
      });
    });
  }

  getObjectId(id) {
    return new ObjectId(id);
  }

  insertOne(collectionName, doc) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        db.collection(collectionName).insertOne(doc, (err, res) => {
          if (err) {
            reject(err);
          }
          resolve(res);
        });
      });
    });
  }

  insertMany(collectionName, docs) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        db.collection(collectionName).insertMany(docs, (err, res) => {
          if (err) {
            reject(err);
          }
          resolve(res);
        });
      });
    });
  }

  deleteOne(collectionName, doc) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        db.collection(collectionName).deleteOne(doc, (err, res) => {
          if (err) {
            reject(err);
          }
          resolve(res);
        });
      });
    });
  }

  updateOne(collectionName, filter, update) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        db.collection(collectionName).updateOne(filter, update, (err, res) => {
          if (err) {
            reject(err);
          }
          resolve(res);
        });
      });
    });
  }

  findOne(collectionName, filter) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        db.collection(collectionName).findOne(filter, (err, res) => {
          if (err) {
            reject(err);
          }
          resolve(res);
        });
      });
    });
  }

  find(collectionName, filter, fields = {}, options = {}) {
    const { page = 1, pageSize = 10, sort = {} } = options;
    const skipNum = (page - 1) * pageSize;
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        const res = db
          .collection(collectionName)
          .find(filter, { fields })
          .skip(skipNum)
          .limit(pageSize)
          .sort(sort);
        res.toArray((err, docs) => {
          if (err) {
            reject(err);
          }
          resolve(docs);
        });
      });
    });
  }

  count(collectionName, filter) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        db.collection(collectionName).count(filter, (err, res) => {
          if (err) {
            reject(err);
          }
          resolve(res);
        });
      });
    });
  }
}

module.exports = MongoSir.getInstance();
